﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using ProtoBuf.Meta;

namespace Core
{
    public class MessageSystem : EntitySystem, IAwake
    {
        public Dictionary<ushort, MessageInfo> messageIDs = new Dictionary<ushort, MessageInfo>();
        public Dictionary<Type, MessageInfo> messageTypes = new Dictionary<Type, MessageInfo>();

        public void Awake()
        {
            Subscribe<DeserializeMessage>(DeserializeMessage);
            Subscribe<SerializeMessage>(SerializeMessage);
            Subscribe<AnalysisMessageAssembly>(AnalysisMessageAssembly);
        }

        public void SerializeMessage(ref SerializeMessage data)
        {
            var message = data.message;

            if (!TryGetInfo(message.GetType(), out var info))
            {
                new CoreTip(TipLevel.Error, $"找不到对应的消息信息，序列化失败: {message.GetType()}").Publish();
                return;
            }

            var bodyBytes = Serialize(message);
            var messageLength = bodyBytes.Length + Config.MESSAGE_BODY_POSITION;
            if (messageLength > Config.MAX_MESSAGE_SIZE)
            {
                new CoreTip(TipLevel.Error, $"消息长度过大，序列化失败:{Config.MAX_MESSAGE_SIZE}, 当前长度:{messageLength}, 类型:{message.GetType()}").Publish();
                return;
            }

            var messageBuffer = MessageBuffer.Pop();
            messageBuffer.messageLength = messageLength;
            data.messageBuffer = messageBuffer;

            // 消息长度
            var lengthBytes = BitConverter.GetBytes(messageLength);
            Array.Copy(lengthBytes, 0, messageBuffer.buffer, Config.MESSAGE_LENGTH_POSITION, Config.MESSAGE_LENGTH_SIZE);

            // rpcID
            var rpcValue = data.rpcID;
            if (data.isTranspond)
                rpcValue |= Config.RPC_TRANSPOND_MASK;

            if (data.rpcID > 0 && message is IResponseMessage)
                rpcValue |= Config.RPC_RESPONSE_MASK;

            messageBuffer.buffer[Config.RPC_ID_POSITION] = rpcValue;

            // sid
            var uidBytes = BitConverter.GetBytes(data.sessionID);
            Array.Copy(uidBytes, 0, messageBuffer.buffer, Config.SESSION_ID_POSITION, Config.SESSION_ID_SIZE);

            // mid
            var midBytes = BitConverter.GetBytes(info.ID);
            Array.Copy(midBytes, 0, messageBuffer.buffer, Config.MESSAGE_ID_POSITION, Config.MESSAGE_ID_SIZE);

            // 消息体
            Array.Copy(bodyBytes, 0, messageBuffer.buffer, Config.MESSAGE_BODY_POSITION, bodyBytes.Length);
        }

        public void DeserializeMessage(ref DeserializeMessage data)
        {
            var buffer = data.buffer;
            var length = data.length;

            var messageID = BitConverter.ToUInt16(buffer, Config.MESSAGE_ID_POSITION);
            if (!TryGetType(messageID, out var messageType))
            {
                new CoreTip(TipLevel.Error, $"找不到对应的消息类型，ID：{messageID}").Publish();
                return;
            }

            var bodyLenght = length - Config.MESSAGE_BODY_POSITION;

            var obj = Deserialize(messageType, buffer, Config.MESSAGE_BODY_POSITION, bodyLenght);
            if (obj == null)
            {
                new CoreTip(TipLevel.Error, $"解析出错：{messageType}").Publish();
                return;
            }

            data.message = obj as IMessage;
        }

        /// <summary>
        /// 将字节数组反序列化为对象实例——ProtoBuf
        /// </summary>
        /// <param name="type">对象类型</param>
        /// <param name="arr">字节数组</param>
        /// <returns></returns>
        public object Deserialize(Type type, byte[] arr, int index, int count)
        {
            using (MemoryStream ms = new MemoryStream(arr, index, count))
                return RuntimeTypeModel.Default.Deserialize(ms, null, type);
        }

        /// <summary>
        /// 将对象实例序列化为字节数组——ProtoBuf
        /// </summary>
        /// <param name="obj">对象实例</param>
        /// <returns></returns>
        public byte[] Serialize(object obj)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize(ms, obj);
                return ms.ToArray();
            }
        }

        public void AnalysisMessageAssembly(ref AnalysisMessageAssembly data)
        {
            var types = data.messageAssembly.GetTypes();
            foreach (var dataType in types)
            {
                var att = dataType.GetCustomAttribute(typeof(MessageDataAttribute)) as MessageDataAttribute;
                if (att == null)
                    continue;

                if (messageIDs.TryGetValue(att.messageID, out var info))
                {
                    new CoreTip(TipLevel.Error, $"已存在相同消息的协议ID，请重新设置ID。已存在：{info}, 尝试添加的：{dataType.FullName}").Publish();
                    continue;
                }

                info = new MessageInfo();
                info.ID = att.messageID;
                info.messageObjType = dataType;

                messageTypes.Add(dataType, info);
                messageIDs.Add(att.messageID, info);
            }
        }

        public bool TryGetType(ushort messageID, out Type objType)
        {
            objType = null;
            if (!messageIDs.TryGetValue(messageID, out var info))
                return false;

            objType = info.messageObjType;
            return true;
        }

        public bool TryGetID(Type objType, out ushort messageID)
        {
            messageID = 0;
            if (!messageTypes.TryGetValue(objType, out var info))
                return false;

            messageID = info.ID;
            return true;
        }

        public bool TryGetInfo(Type objType, out MessageInfo info)
        {
            return messageTypes.TryGetValue(objType, out info);
        }
    }

    public struct MessageInfo
    {
        public ushort ID;
        public Type messageObjType;

        public override string ToString()
        {
            return $"ID:{ID}, type:{messageObjType}";
        }
    }

    public struct AnalysisMessageAssembly : IEventData
    {
        public Assembly messageAssembly;

        public AnalysisMessageAssembly(Assembly messageAssembly)
        {
            this.messageAssembly = messageAssembly;
        }
    }

    public struct DeserializeMessage : IEventData
    {
        public byte[] buffer;
        public int length;
        public IMessage message;

        public DeserializeMessage(byte[] buffer, int length)
        {
            this.buffer = buffer;
            this.length = length;
            this.message = null;
        }
    }

    public struct SerializeMessage : IEventData
    {
        public IMessage message;
        public uint sessionID;
        public byte rpcID;
        public bool isTranspond;

        public SerializeMessage(IMessage message, uint userID, byte rpcID, bool isTranspond)
        {
            this.rpcID = rpcID;
            this.message = message;
            this.sessionID = userID;
            this.isTranspond = isTranspond;

            this.messageBuffer = null;
        }

        public MessageBuffer messageBuffer;
    }
}
